﻿using Devil;
using LitJson;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

namespace GameToolkit.Editor
{

    [CustomPropertyDrawer(typeof(GameInputAttribute), true)]
    public class GameInputDrawer : PropertyDrawer
    {
        // <group, inputs>
        static Dictionary<int, GUIContent[]> sButtons;
        static Dictionary<int, GUIContent[]> sAxis;
        static Dictionary<int, GUIContent[]> sInputs;
        static GUIContent[] sDefaultArray = new GUIContent[] { new GUIContent("<none>", "")};

        [InitializeOnLoadMethodParallel(IsOnMainThread = false)]
        static void Initialize()
        {
            var file = NativeFileSystem.ReadAllText("Assets/Resources/InputDefine/Input.json");
            if (string.IsNullOrEmpty(file))
                return;
            var content = JsonMapper.ToObject(file);
            if (content == null)
                return;
            sButtons = new Dictionary<int, GUIContent[]>();
            sAxis = new Dictionary<int, GUIContent[]>();
            sInputs = new Dictionary<int, GUIContent[]>();
            // input data: axis?, desc, categories
            var inputs = new Dictionary<string, ArrayList>();
            foreach(var key in content.Keys)
            {
                var inputDefs = content[key];
                foreach(var input in inputDefs.Keys)
                {
                    ArrayList lst;
                    var axis = (bool)inputDefs[input]["axis"];
                    var desc = (string)inputDefs[input]["desc"];
                    if (string.IsNullOrEmpty(desc))
                        desc = input;
                    if (!inputs.TryGetValue(input, out lst))
                    {
                        lst = new ArrayList();
                        lst.Add(axis);
                        lst.Add(desc);
                        inputs[input] = lst;
                    }
                    else if (axis != (bool)lst[0])
                    {
                        lst = new ArrayList();
                        lst.Add(axis);
                        lst.Add(desc);
                        inputs[$"$${input}"] = lst;
                    }
                    lst.Add(key);
                }
            }
            var btnArr = new Dictionary<int, List<GUIContent>>();
            var axisArr = new Dictionary<int, List<GUIContent>>();
            var allArr = new Dictionary<int, List<GUIContent>>();
            foreach (var kv in inputs)
            {
                var inputName = kv.Key;
                var isAll = !inputName.StartsWith("$$");
                if (!isAll)
                {
                    inputName = inputName.Substring(2);
                }
                var arr = kv.Value;
                var isAxis = (bool)arr[0];
                var prefix = isAxis ? "A" : "B";
                for (int i = 2; i < arr.Count; i++)
                {
                    var groupName = (string)arr[i];
                    var group = groupName.IgnoreCaseToHash();
                    var label = new GUIContent($"{inputName} ({groupName}: {arr[1]}, {prefix})", inputName);
                    var dic = isAxis ? axisArr : btnArr;
                    AddInputLabel(dic, group, label);
                    if(isAll)
                    {
                        AddInputLabel(allArr, group, label);
                    }
                }
            }
            foreach(var kv in btnArr)
            {
                sButtons[kv.Key] = kv.Value.ToArray();
            }
            foreach(var kv in axisArr)
            {
                sAxis[kv.Key] = kv.Value.ToArray();
            }
            foreach(var kv in allArr)
            {
                sInputs[kv.Key] = kv.Value.ToArray();
            }
        }

        static void AddInputLabel(Dictionary<int, List<GUIContent>> dic, int group, GUIContent label)
        {
            List<GUIContent> lst;
            if (!dic.TryGetValue(group, out lst))
            {
                lst = new List<GUIContent>();
                lst.Add(sDefaultArray[0]);
                dic[group] = lst;
            }
            lst.Add(label);
            if (group != 0)
            {
                if (!dic.TryGetValue(0, out lst))
                {
                    lst = new List<GUIContent>();
                    lst.Add(sDefaultArray[0]);
                    dic[0] = lst;
                }
                lst.Add(label);
            }
        }

        static int GetIndex(GUIContent[] arr, string inputName)
        {
            for (int i = 1; i < arr.Length; i++)
            {
                if (inputName.EqualsIgnoreCase(arr[i].tooltip))
                    return i;
            }
            return 0;
        }

        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            var att = attribute as GameInputAttribute;
            label = EditorGUI.BeginProperty(position, label, property);
            position = EditorGUI.PrefixLabel(position, label);
            var lv = EditorGUI.indentLevel;
            EditorGUI.indentLevel = 0;
            if (!ParallelUnity.IsEditorInitialized)
            {
                EditorGUI.LabelField(position, "The system is initializing.");
            }
            else if (property.propertyType != SerializedPropertyType.String)
            {
                EditorGUI.LabelField(position, "The property type is not supported.");
            }
            else
            {
                var dic = att is GameButtonAttribute ? sButtons : (att is GameAxisAttribute ? sAxis : sInputs);
                GUIContent[] inputs;
                if (dic == null || !dic.TryGetValue(att.Category.IgnoreCaseToHash(), out inputs))
                    inputs = sDefaultArray;
                var index = GetIndex(inputs, property.stringValue);
                var sel = EditorGUI.Popup(position, index, inputs);
                if (sel != index)
                {
                    property.stringValue = inputs[sel].tooltip;
                    GUI.changed = true;
                }
            }
            EditorGUI.indentLevel = lv;
            EditorGUI.EndProperty();
        }
    }
}
